package gu.simplemq.pool;

import java.io.Closeable;
import java.net.URI;
import java.util.concurrent.atomic.AtomicInteger;
import static com.google.common.base.Preconditions.checkState;

/**
 * 支持嵌套调用的消息系统连接对象管理基类
 * @author guyadong
 *
 * @param <R> 资源池管理的对象类型
 */
public abstract class BaseMQPool<R> implements Closeable{
	protected volatile boolean closed = false;
	/** apply/free 嵌套计数 */
	private final ThreadLocal<AtomicInteger> tlsNestCount  = new ThreadLocal<AtomicInteger>(){
		@Override
		protected AtomicInteger initialValue() {
			return new AtomicInteger(0);
		}
	};

	/**
	 * 当前线程下的资源对象
	 */
	private final ThreadLocal<R> tlsResource = new ThreadLocal<R>();

	protected BaseMQPool() {
	}
	/**
	 * 从资源池获取一个资源对象<br>
	 * 对于调用此方法获取的实例，当不再使用时需要调用{@link #release(Object)}释放<br>
	 * @return 资源对象
	 */
	public abstract R borrow();
    
    /**
     * 将资源对象归还到资源池<br>
     * @param r 资源对象
     */
    public abstract void release(R r) ;
    
    /** 
	 * 申请当前线程使用的资源对象,不可跨线程使用<br>
	 * 使用完后调用 {@link #free()}释放对象
	 * @throws MQPoolException
	 */
	public final R apply() throws MQPoolException{
		R r = tlsResource.get();
		if(null == r){
			r = borrow();
			this.tlsResource.set(r);
		}
    	if(tlsNestCount.get() == null){
    		tlsNestCount.set(new AtomicInteger(0));
    	}
		tlsNestCount.get().incrementAndGet();
		return r;
	}
	/**
	 * 释放当前线程使用的资源
	 * 必须与{@link #apply()}配对使用
	 */
	public final void free(){
		R r = tlsResource.get();
		checkState(r != null && tlsNestCount.get() != null, "apply/free mismatch");		
		if(0 == tlsNestCount.get().decrementAndGet()){
			release(r);
			tlsResource.remove();
			tlsNestCount.remove();
		}
	}

	/**
	 * @return
	 */
	public abstract URI getCanonicalURI();
	
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append(getClass().getSimpleName());
		builder.append("@");
		builder.append(hashCode());	
		builder.append("[URI=");
		builder.append(getCanonicalURI());
		builder.append("]");
		return builder.toString();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof BaseMQPool)) {
			return false;
		}
		@SuppressWarnings("rawtypes")
		BaseMQPool other = (BaseMQPool) obj;		
		return getCanonicalURI().equals(other.getCanonicalURI());
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		URI uri = getCanonicalURI();
		result = prime * result + ((uri == null) ? 0 : uri.hashCode());
		return result;
	}
    /**
     * Has this pool instance been closed.
     * @return <code>true</code> when this pool has been closed.
     */
	public boolean isClosed() {
		return closed;
	}
	@SuppressWarnings("serial")
	public static class MQPoolException extends RuntimeException{
	
		public MQPoolException(Throwable cause) {
			super(cause);
		}
	}

}
